package com.seal.commons.copiers;

import com.seal.commons.copiers.cache.MapperKey;
import com.seal.commons.copiers.cglib.CglibCopier;
import com.seal.commons.copiers.orika.OrikaCopier;
import lombok.Getter;
import net.sf.cglib.core.Converter;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 功能描述: <br/>
 *
 * @author huangyong
 * @date: 2020-05-31 22:38<br/>
 * @since JDK 1.8
 */
public final class CopierFactory {

    private static final String ORIKA = "orika";
    private static final String CGLIB = "cglib";

    public static <S, T> OrikaCopier<S, T> getOrCreateOrikaCopier(Class<S> sourceClass, Class<T> targetClass) {
        ConcurrentMap<MapperKey<S, T>, OrikaCopier<S, T>> orikaCache = CopierCache.<S, T>getInstance().getOrikaCache();
        MapperKey<S, T> mapperKey = new MapperKey<>(ORIKA, sourceClass, targetClass);
        return orikaCache.computeIfAbsent(mapperKey, key -> new OrikaCopier.Builder<>(key.getSourceClass(), key.getTargetClass()).register());
    }

    public static <S, T> CglibCopier<S, T> getOrCreateCglibCopier(Class<S> sourceClass, Class<T> targetClass) {
        ConcurrentMap<MapperKey<S, T>, CglibCopier<S, T>> cglibCache = CopierCache.<S, T>getInstance().getCglibCache();
        MapperKey<S, T> mapperKey = new MapperKey<>(CGLIB, sourceClass, targetClass);
        return cglibCache.computeIfAbsent(mapperKey, key -> new CglibCopier<>(key.getSourceClass(), key.getTargetClass()));
    }

    public static <S, T> CglibCopier<S, T> getOrCreateCglibCopier(Class<S> sourceClass, Class<T> targetClass, Converter converter) {
        ConcurrentMap<MapperKey<S, T>, CglibCopier<S, T>> cglibCache = CopierCache.<S, T>getInstance().getCglibCache();
        MapperKey<S, T> mapperKey = new MapperKey<>(CGLIB, sourceClass, targetClass, converter);
        return cglibCache.computeIfAbsent(mapperKey, key -> new CglibCopier<>(key.getSourceClass(), key.getTargetClass(), converter));
    }

    public static class CopierCache<S, T> {

        private static volatile CopierCache<?, ?> INSTANCE;

        @SuppressWarnings("unchecked")
        public static <S, T> CopierCache<S, T> getInstance() {
            if (INSTANCE == null) {
                synchronized (CopierCache.class) {
                    if (INSTANCE == null) {
                        init();
                    }
                }
            }
            return (CopierCache<S, T>) INSTANCE;
        }

        private static void init() {
            INSTANCE = new CopierCache();
            CopierCache<?, ?> instance = INSTANCE;
            instance.cglibCache = new ConcurrentHashMap<>(1024, 0.75f);
            instance.orikaCache = new ConcurrentHashMap<>(1024, 0.75f);
        }

        @Getter
        private ConcurrentMap<MapperKey<S, T>, CglibCopier<S, T>> cglibCache;

        @Getter
        private ConcurrentMap<MapperKey<S, T>, OrikaCopier<S, T>> orikaCache;

    }
}